package dk.pun.aot.server;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import dk.pun.aot.client.LoginService;
import dk.pun.aot.shared.character.CharacterData;
import dk.pun.aot.shared.character.CharacterId;
import dk.pun.aot.shared.user.LoginInfo;
import dk.pun.aot.shared.user.UserData;

@SuppressWarnings("serial")
public class LoginServiceImpl extends RemoteServiceServlet implements LoginService {

	private static final Logger LOG = Logger.getLogger(LoginServiceImpl.class.getName());
	private static final String anonymousUserId = "1";
	private static final int anonymousCharacterExpiration = 24*60*60*1000;

	@Override
	public LoginInfo login(String requestURL) {
		UserService userService = UserServiceFactory.getUserService();
		User user = userService.getCurrentUser();
		LoginInfo loginInfo = new LoginInfo();
		
		if (user != null) {
			loginInfo.setLoggedIn(true);
			loginInfo.setLogoutURL(userService.createLogoutURL(requestURL));
			loginInfo.setUserData(this.loadUserData(user));
			loginInfo.setUserAdmin(userService.isUserAdmin());
			LOG.fine("Login info: [userId=" + user.getUserId() + ", nickname=" + user.getNickname() + ", email=" + user.getEmail() + "]");
		} else {
			loginInfo.setLoggedIn(false);
			loginInfo.setLoginURL(userService.createLoginURL(requestURL));
		}
		return loginInfo;
	}

	@Override
	public LoginInfo anonymousLogin() {
		this.cleanupAnonymousCharacters();
		LoginInfo loginInfo = new LoginInfo();
		loginInfo.setLoggedIn(true);
		loginInfo.setAnonymous(true);
		loginInfo.setUserData(this.loadUserData(null));
		loginInfo.setLoginURL("/");
		loginInfo.setLogoutURL("/");
		LOG.fine("Login info: [userId=" + loginInfo.getUserData().getUserId() + ", nickname=" + loginInfo.getUserData().getNickname() + ", email=" + loginInfo.getUserData().getEmail() + "]");
		return loginInfo;
	}

	private UserData loadUserData(User user) {
		PersistenceManager pm = PMF.getPersistenceManager();
		UserData userData;
		try {
			Query q = pm.newQuery(UserData.class, "userId == currentUserId");
			q.declareParameters("String currentUserId");
			String userId;
			if (user != null) {
				userId = user.getUserId();
			} else {
				userId = anonymousUserId;
			}
			@SuppressWarnings("unchecked")
			List<UserData> results = (List<UserData>) q.execute(userId);
			if (results.size() == 0) {
				userData = new UserData(userId);
				pm.makePersistent(userData);
			} else {
				userData = results.get(0);
			}
			if (user != null) {
				userData.setNickname(user.getNickname());
				userData.setEmail(user.getEmail());
			} else {
				userData.setNickname("Anonymous");
			}
			userData = pm.detachCopy(userData);
		} finally {
			pm.close();
		}
		return userData;
	}

	private void cleanupAnonymousCharacters() {
		PersistenceManager pm = PMF.getPersistenceManager();
		try {
			Query q = pm.newQuery(UserData.class, "userId == currentUserId");
			q.declareParameters("String currentUserId");
			@SuppressWarnings("unchecked")
			List<UserData> results = (List<UserData>) q.execute(anonymousUserId);
			if (results.size() > 0) {
				UserData userData = results.get(0);
				Iterator<CharacterId> characterIdIterator = userData.getCharacterIdIterator();
				while (characterIdIterator.hasNext()) {
					CharacterId characterId = characterIdIterator.next();
					CharacterData characterData = pm.getObjectById(CharacterData.class, characterId.getKey());
					Date expirationTime = new Date(System.currentTimeMillis() - anonymousCharacterExpiration);
					if (characterData != null && characterData.getCreatedDate().before(expirationTime)) {
						pm.deletePersistent(characterData);
						userData.removeCharacterId(characterId);
					} else if (characterData == null) {
						userData.removeCharacterId(characterId);
					}
				}
			}
		} finally {
			pm.close();
		}
	}

}
